/*
**  Copyright (c) 2014 Michael Liu(michael.liu.point@gmail.com).
**
**  Project: Gateway Unified Management Platform
**  File:    dpdev.c
**  Author:  Michael
**  Date:    06/24/2014
**
**  Purpose:
**    data path interface methods.
*/

/* Include files. */

#include "dpmgr.h"
#include "osdep.h"
#include "pfdep.h"

/* Macro constant definitions. */

/* Type definitions. */

/* Local function declarations. */

static e_ret dp_dev_method_add_ref_if(tdp_dev * dp_dev, void *dp_if);
static e_ret dp_dev_method_del_ref_if(tdp_dev * dp_dev, void *dp_if);

/* Macro API definitions. */

/* Global variable declarations. */

e_ret dp_dev_prepare(tdp_dev * dp_dev, e_ret(*init) (tdp_dev * dp_dev), e_ret(*clean) (tdp_dev * dp_dev))
{

	sysmgr_trace_enter();

	ASSERT(dp_dev != NULL);

	if (dp_dev->prepare == eBOOL_TRUE) {
		sysmgr_dbg("data path device [%s] already prepared.\n", dp_dev->name);
		sysmgr_trace_exit();
		return (eRET_SUCCESS);
	}

	sysmgr_dbg("data path device [%s] prepare.\n", dp_dev->name);

	dp_dev->ref_ifs_cnt = 0;
	dp_dev->add_ref_if = dp_dev_method_add_ref_if;
	dp_dev->del_ref_if = dp_dev_method_del_ref_if;
	dp_dev->init = init;
	dp_dev->clean = clean;
	dp_dev->prepare = eBOOL_TRUE;

	sysmgr_trace_exit();
	return (eRET_SUCCESS);
}

e_ret dp_dev_method_add_ref_if(tdp_dev * dp_dev, void *dp_if)
{
	int i;
	e_ret ret;

	sysmgr_trace_enter();

	if (dp_dev->ref_ifs_cnt == 0) {
		sysmgr_dbg("dp dev[%s] first reference if[%s] added, do init() = %p\n",
			   dp_dev->name, ((tdp_if *) dp_if)->name, dp_dev->init);
		if (dp_dev->init != NULL) {
			dp_dev->init(dp_dev);
			dp_dev->ref_ifs_cnt = 1;
			dp_dev->ref_ifs[0] = dp_if;
		}
		ret = eRET_SUCCESS;
	} else {
		sysmgr_dbg("dp dev[%s] no.%d reference if[%s] added.\n",
			   dp_dev->name, dp_dev->ref_ifs_cnt + 1, ((tdp_if *) dp_if)->name);
		for (i = 0; i < DP_DEV_REF_IF_MAX; i++) {
			if (dp_dev->ref_ifs[i] == NULL) {
				dp_dev->ref_ifs[i] = dp_if;
				dp_dev->ref_ifs_cnt++;
				break;
			}
		}
		if (i >= DP_DEV_REF_IF_MAX) {
			ret = eRET_NO_RESOURCE;
		} else {
			ret = eRET_SUCCESS;
		}
	}

	sysmgr_trace_exit();
	return (ret);
}

e_ret dp_dev_method_del_ref_if(tdp_dev * dp_dev, void *dp_if)
{
	int i;
	e_ret ret;

	sysmgr_trace_enter();

	for (i = 0; i < DP_DEV_REF_IF_MAX; i++) {
		if (dp_dev->ref_ifs[i] == dp_if) {
			dp_dev->ref_ifs[i] = NULL;
			dp_dev->ref_ifs_cnt--;
			break;
		}
	}

	if (i >= DP_DEV_REF_IF_MAX) {
		ret = eRET_NOT_EXIST;
	} else {
		if (dp_dev->ref_ifs_cnt == 0) {
			sysmgr_dbg("dp dev[%s] last reference if[%s] removed, do clean() = %p\n",
				   dp_dev->name, ((tdp_if *) dp_if)->name, dp_dev->clean);
			if (dp_dev->clean != NULL) {
				dp_dev->clean(dp_dev);
			}
		} else {
			sysmgr_dbg("dp dev[%s] no.%d reference if[%s] removed.\n",
				   dp_dev->name, dp_dev->ref_ifs_cnt, ((tdp_if *) dp_if)->name);
		}
		ret = eRET_SUCCESS;
	}

	sysmgr_trace_exit();
	return (ret);
}
